package com.arpa.oms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.arpa.core.utils.collection.CollectionUtil;
import com.arpa.core.utils.misc.IdGenerator;
import com.arpa.core.utils.number.NumberUtil;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.cache.AreaCache;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.domain.entity.*;
import com.arpa.ntocc.common.mapper.AreaCityMapper;
import com.arpa.ntocc.common.mapper.AreaCountryMapper;
import com.arpa.ntocc.common.mapper.AreaCountyMapper;
import com.arpa.ntocc.common.mapper.AreaProvinceMapper;
import com.arpa.ntocc.common.service.*;
import com.arpa.oms.common.util.DataMaskingUtils;
import com.arpa.oms.domain.dto.*;
import com.arpa.oms.domain.entity.*;
import com.arpa.oms.domain.enums.OmsGoodTypeEnum;
import com.arpa.oms.domain.enums.OmsSaleUnpackStatusEnum;
import com.arpa.oms.domain.enums.OrderStatusEnum;
import com.arpa.oms.domain.enums.SaleTypeEnum;
import com.arpa.oms.domain.vo.OmsSaleExcelVO;
import com.arpa.oms.domain.vo.SaleItemVO;
import com.arpa.oms.domain.vo.SaleVO;
import com.arpa.oms.mapper.SaleMapper;
import com.arpa.oms.service.*;
import com.arpa.wms.cache.*;
import com.arpa.wms.common.util.OrderIdGeneratorUtil;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.CarrierTypeEnum;
import com.arpa.wms.domain.enums.StatusEnum;
import com.arpa.wms.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.arpa.wms.domain.consts.NumConst.*;
import static com.arpa.wms.domain.consts.TipConst.*;

/**
 * <p>
 * OMS销售单 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-10-22
 */
@Service
@Log4j2(topic = "business")
public class SaleServiceImpl extends ServiceImpl<SaleMapper, Sale> implements ISaleService {

    private final WarehouseCache warehouseCache;

    private final PartyCache partyCache;

    private final ISaleItemService iSaleItemService;

    private final IOmsOutboundService iOmsOutboundService;

    private final IDeliveryService iDeliveryService;

    private final IOmsInventoryService iOmsInventoryService;

    private final GoodsCache goodsCache;

    private final CustomerCache customerCache;

    private final GoodsUnitConversionCache goodsUnitConversionCache;

    @Autowired
    private ShopCache shopCache;
    @Autowired
    private CarrierCache carrierCache;
    @Autowired
    private IOrderDealService orderDealService;
    @Autowired
    private IOmsAutoCheckService omsAutoCheckService;
    @Autowired
    private IOmsAutoCheckGoodsService omsAutoCheckGoodsService;
    @Autowired
    private ISaleOrderService iSaleOrderService;
    @Autowired
    private IShipmentService iShipmentService;
    @Autowired
    private IGoodsService iGoodsService;
    @Autowired
    private IGoodsUnitService iGoodsUnitService;
    @Autowired
    private ImportTempCache importTempCache;
    @Autowired
    private IOmsCustomerService iCustomerService;
    @Autowired
    private IShopService iShopService;
    @Autowired
    private ICarrierService iCarrierService;
    @Autowired
    private IOrderDealService iOrderDealService;
    @Resource
    public RedissonClient redissonClient;

    @Autowired
    private IAreaProvinceService iAreaProvinceService;
    @Autowired
    private AreaCache areaCache;

    public SaleServiceImpl(PartyCache partyCache, ISaleItemService iSaleItemService, WarehouseCache warehouseCache,
                           IOmsOutboundService iOmsOutboundService, IDeliveryService iDeliveryService,
                           GoodsUnitConversionCache goodsUnitConversionCache,
                           IOmsInventoryService iOmsInventoryService, GoodsCache goodsCache,CustomerCache customerCache) {
        this.partyCache = partyCache;
        this.iSaleItemService = iSaleItemService;
        this.warehouseCache = warehouseCache;
        this.iOmsOutboundService = iOmsOutboundService;
        this.iDeliveryService = iDeliveryService;
        this.iOmsInventoryService = iOmsInventoryService;
        this.goodsCache = goodsCache;
        this.customerCache = customerCache;
        this.goodsUnitConversionCache = goodsUnitConversionCache;
    }

    // XYTODO TODO 计算百分数，除以100时计算的常量    后期可统一放到公用文件中
    public static final BigDecimal DECIMAL100 = new BigDecimal("100");

    /**
     * 保存
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor={Exception.class})
    public boolean save(Sale entity) {
        if(StringUtils.isBlank(entity.getCode())){
            entity.setCode(IdUtil.simpleUUID());
        }
        entity.setShopName(shopCache.translate(entity.getShopCode()));
        entity.setCarrierName(carrierCache.translate(entity.getCarrierCode()));
        if(StringUtils.isBlank(entity.getCreatedBy())){
            entity.setCreatedBy(UserUtil.getCode());
            entity.setCreatedName(partyCache.translate(UserUtil.getCode()));
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }
        List<String> areaList = entity.getAreaList();
        if (IterUtil.isNotEmpty(areaList) && areaList.size() == 4) {
            entity.setCountryName(areaList.get(0));
            entity.setProvinceName(areaList.get(1));
            entity.setCityName(areaList.get(2));
            entity.setCountyName(areaList.get(3));
        }
        return super.save(entity);
    }


    /**
     * 查询列表
     *
     * @param dto
     * @return
     */
    @Override
    public List<SaleVO> queryList(SaleDto dto) {

        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StrUtil.isNotEmpty(dto.getSortField())) {
            dto.setSortField(CommonUtil.camel2Underline(dto.getSortField()));
        }
        List<SaleVO> saleVOS = baseMapper.queryList(dto);
        saleVOS.forEach(info ->{
            //脱敏处理
            info.setBuyerMobile(DataMaskingUtils.mobileEncrypt(info.getBuyerMobile()));
            info.setBuyerName(DataMaskingUtils.nameEncrypt(info.getBuyerName()));
            info.setBuyerAddress(DataMaskingUtils.addressEncrypt(info.getBuyerAddress()));
        });
        return saleVOS;
    }

    /**
     * 查询合计,包含总数
     *
     * @param dto
     * @return
     */
    @Override
    public SaleVO queryListSum(SaleDto dto) {
        SaleVO saleVo = baseMapper.queryListSum(dto);
        return saleVo;
    }

    /**
     * 通过code查询销售单详细信息
     *
     * @param code 销售单号
     * @return
     */
    @Override
    public SaleVO getByCode(String code) {
        Sale sale = getOne(new QueryWrapper<Sale>().lambda().eq(Sale::getCode,code));
        if(sale!=null){
            SaleVO saleVo = new SaleVO();
            BeanUtil.copyProperties(sale,saleVo,false);
            List<SaleItem> saleItems = iSaleItemService.list(new QueryWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode,saleVo.getCode()));
            saleVo.setWarehouseName(warehouseCache.translate(saleVo.getWarehouseCode()));
            saleVo.setCustomerName(customerCache.translate(saleVo.getCustomerCode()));

            List<String> goodsCodeList = saleItems.stream().map(v -> v.getGoodsCode()).collect(Collectors.toList());
            List<OmsInventory> inventoryList = iOmsInventoryService.list(new LambdaQueryWrapper<OmsInventory>().eq(OmsInventory::getWarehouseCode, sale.getWarehouseCode()).in(OmsInventory::getGoodsCode, goodsCodeList));
            Map<String, OmsInventory> inventoryMap = inventoryList.stream().collect(Collectors.toMap(OmsInventory::getGoodsCode, t -> t));

            List<SaleItemVO> saleItemVOS = saleItems.stream().map(r->{
                SaleItemVO itemVO = new SaleItemVO();
                BeanUtil.copyProperties(r, itemVO);
                List<GoodsUnitConversion> goodsUnitConversions = goodsUnitConversionCache.get(r.getGoodsCode());
                itemVO.setGoodsUnitConversionList(goodsUnitConversions);
                //销售单审核时，明细查找需要来源单号字段
                itemVO.setSourceCode(sale.getSourceCode());
              /*  if (inventoryMap != null && inventoryMap.containsKey(itemVO.getGoodsCode())) {
                    OmsInventory inventory = inventoryMap.get(itemVO.getGoodsCode());
                    if (null != inventory) {
                        BigDecimal num = inventory.getQuantity().subtract(inventory.getUseQuantity()).subtract(inventory.getContUseQuantity());
                        itemVO.setActualQuantity(num);
                    }
                }*/
                return itemVO;
            }).collect(Collectors.toList());
            List<String> areaList = new ArrayList<>();
            areaList.add(sale.getCountryName());
            areaList.add(sale.getProvinceName());
            areaList.add(sale.getCityName());
            areaList.add(sale.getCountyName());
            saleVo.setAreaList(areaList);
            saleVo.setSaleItemList(saleItemVOS);
            return saleVo;
        }
        return null;
    }

    /**
     * 销售单作废
     *
     * @param code 销售单号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void annul(String code) {
        Sale sale = getOne(new QueryWrapper<Sale>().lambda().eq(Sale::getCode,code).in(Sale::getStatus,OrderStatusEnum.NEWS.getValue(),OrderStatusEnum.NO_PASS.getValue()));
        if(null == sale){
            throw new ServiceException("销售单不存在或不允许作废");
        }else{
            sale.setModifiedBy(UserUtil.getCode());
            sale.setModifiedName(partyCache.translate(UserUtil.getCode()));
            sale.setStatus(OrderStatusEnum.ANNUL.getValue());
            updateById(sale);
        }

    }

    /**
     * 创建销售单
     *
     * @param sale
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(Sale sale) {
        sale.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SO, sale.getShipmentCode(),UserUtil.getBranchCode()));
        sale.setSaleType(SaleTypeEnum.NORMAL.getValue());
        sale.setCreatedBy(UserUtil.getCode());
        sale.setCreatedName(partyCache.translate(UserUtil.getCode()));
        sale.setModifiedBy(UserUtil.getCode());
        sale.setModifiedName(partyCache.translate(UserUtil.getCode()));
        sale.setStatus(OrderStatusEnum.NEWS.getValue());
        sale.setShopName(shopCache.translate(sale.getShopCode()));
        sale.setCarrierName(carrierCache.translate(sale.getCarrierCode()));
        // 计算订单总数（销售单位、基本单位）
        calcQuantity(sale, true);

        //保存销售单
        save(sale);

        sale.getSaleItemList().forEach(r->{
            r.setCode(IdUtil.simpleUUID());
            r.setSaleCode(sale.getCode());
            r.setCreatedBy(sale.getCreatedBy());
            r.setCreatedName(sale.getCreatedName());
            r.setModifiedBy(sale.getModifiedBy());
            r.setModifiedName(sale.getModifiedName());
        });
        //保存销售明细
        iSaleItemService.saveBatch(sale.getSaleItemList());
    }

    /**
     * 提交销售单
     *
     * @param sale
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submit(Sale sale) {
        // 有单号，则校验单据状态
        Sale dbEntity = null;
        if (StrUtil.isNotBlank(sale.getCode())) {
            dbEntity = this.getOne(new QueryWrapper<Sale>().lambda().eq(Sale::getCode, sale.getCode()));
            if (OrderStatusEnum.PASS.getValue().equals(dbEntity.getStatus()) || OrderStatusEnum.ANNUL.getValue().equals(dbEntity.getStatus())) {
                throw new ServiceException("销售单当前状态无法进行提交操作");
            }
        }

        // 校验明细中是否有重复项
        checkMultipleItem(sale);

        // 计算订单总数（销售单位、基本单位）
        calcQuantity(sale, true);

        // 需要计算当前库存是否有足够的库存支持， 并将库存锁住
        validSaleInventory(sale);

        sale.setShopName(shopCache.translate(sale.getShopCode()));
        sale.setCarrierName(carrierCache.translate(sale.getCarrierCode()));

        // 根据code判断是新增还是修改，然后提交
        sale.setStatus(OrderStatusEnum.PENDING.getValue());
        if (null == dbEntity) {
            sale.setGroupCode(UserUtil.getBranchCode());
            sale.setSaleType(SaleTypeEnum.NORMAL.getValue());
            sale.setCreatedBy(UserUtil.getCode());
            sale.setCreatedName(partyCache.translate(UserUtil.getCode()));
            sale.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SO, sale.getShipmentCode(),UserUtil.getBranchCode()));
        } else {
            sale.setId(dbEntity.getId());
            sale.setModifiedBy(UserUtil.getCode());
            sale.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }
        List<String> areaList = sale.getAreaList();
        if (IterUtil.isNotEmpty(areaList) && areaList.size() == 4) {
            sale.setCountryName(areaList.get(0));
            sale.setProvinceName(areaList.get(1));
            sale.setCityName(areaList.get(2));
            sale.setCountyName(areaList.get(3));
        }

        if(StrUtil.isNotBlank(sale.getCode())){
            //保存销售单
            saveOrUpdate(sale);
            //清除原有所有明细
            iSaleItemService.remove(new QueryWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode,sale.getCode()));

        }else {
            sale.setModifiedBy(UserUtil.getCode());
            sale.setModifiedName(partyCache.translate(UserUtil.getCode()));
            //保存销售单
            save(sale);
        }
        sale.getSaleItemList().forEach(r -> {
            r.setCode(IdUtil.simpleUUID());
            r.setSaleCode(sale.getCode());
            r.setCreatedBy(sale.getCreatedBy());
            r.setCreatedName(sale.getCreatedName());
            r.setModifiedBy(sale.getModifiedBy());
            r.setModifiedName(sale.getModifiedName());
            if (OmsGoodTypeEnum.GOOD_TYPE_COMPOSE.getCode().equals(r.getRecordType())) {
                //组合商品 销售单位使用基本单位
                r.setUnitConvertQuantity(BigDecimal.ONE);
                r.setSaleUnit(r.getBasicUnit());
            }
        });
        //保存销售明细
        iSaleItemService.saveBatch(sale.getSaleItemList());

        //符合自动审核条件的，自动审核订单
//        autoAudit(sale);
    }

    /**
     * 校验库存是否有足够数量支持销售单
     * @param sale
     */
    @Transactional(rollbackFor = {Exception.class})
    public void validSaleInventory(Sale sale) {
        // 遍历明细， 直接锁库存，如果成功，则说明数量足够，如果失败，则数量不足
        sale.getSaleItemList().stream().forEach(e->{
            // 组合商品不判断库存
            if (!OmsGoodTypeEnum.GOOD_TYPE_COMPOSE.getCode().equals(e.getRecordType())) {
                iOmsInventoryService.lockUsefulInventory(sale.getWarehouseCode(), e.getGoodsCode(), e.getGoodsName(), sale.getShipmentCode(), e.getPlanBasicQuantity());
            }
        });
    }

    /**
     * @description
     * 审核销售单
     * @author xuyang
     * @date 2020/11/7 9:46
     * @param sale
     * @param status
     * @param verifyOpinion
     **/
    @Async
    @Override
    @Transactional(rollbackFor = {ServiceException.class})
    public Future<Boolean> audit(Sale sale, String status, String verifyOpinion) {
        List<SalePacks> saleList;
        // 合单后  不操作拆单
        if (StringUtils.isBlank(sale.getMergeCode())) {
            //所有商品
            List<SaleItem> saleItems = iSaleItemService.list(new QueryWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, sale.getCode()));
            sale.setSaleItemList(saleItems);
            //拆单
            saleList = iOrderDealService.unpack(sale);
            if (saleList.size() > 1) {
                //如果有拆单，更新销售单状态
                sale.setUnpackStatus(OmsSaleUnpackStatusEnum.PACK.getValue());
            }
            for (SalePacks salePacks : saleList) {
                Sale s = new Sale();
                BeanUtil.copyProperties(salePacks, s);
                s.setModifiedBy(UserUtil.getCode());
                s.setModifiedName(partyCache.translate(UserUtil.getCode()));
                //处理组合商品
                s = orderDealService.composeToDetail(s);
                afterAuditPass(s);
            }
        } else {
            sale.setModifiedBy(UserUtil.getCode());
            sale.setModifiedName(partyCache.translate(UserUtil.getCode()));
            //处理组合商品
            sale = orderDealService.composeToDetail(sale);
            afterAuditPass(sale);
        }

        return new AsyncResult(true);
    }

    /**
     * wms完成后，根据已更新的销售单明细更新销售单的实际出库数量（销售单位、基本单位）
     *
     * @param code
     */
    @Override
    public void afterWmsUpdateQuantity(String code) {
        this.baseMapper.afterWmsUpdateQuantity(code);
    }


    /**
     * 根据销售单号，释放所有占用库存；情况包括：
     * 1. 销售单审核不通过；
     * 2. wms中断出库，在库存分配环节，分配为空-》不生成拣货单，流程结束；
     * 3. wms中断出库，在拣货单确认环节，退减所有库存-》不生成出库单，流程结束；
     *
     * @param saleCodes
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void releaseUseQuantity(List<String> saleCodes) {
        List<SaleItem> saleItems = iSaleItemService.list(new QueryWrapper<SaleItem>().lambda().in(SaleItem::getSaleCode, saleCodes));
        // 遍历销售明细，根据原计划数量（基本单位），释放占用量
        saleItems.forEach(e->{
            iOmsInventoryService.update(new UpdateWrapper<OmsInventory>().lambda().eq(OmsInventory::getGoodsCode, e.getGoodsCode())
                    .setSql(" use_quantity = use_quantity - " + e.getPlanBasicQuantity())
            );
        });

    }

    /**
     * @param dto :
     * @description 获取可退销售单列表
     * @author xuyang
     * @date 2020/12/14 11:28
     **/
    @Override
    public List<Sale> findCanRefundList(SaleDto dto) {
        // 根据"实际销售数量 - 退货数量 > 0" 来判断
        List<Sale> sales = list(new QueryWrapper<Sale>().lambda().eq(Sale::getShipmentCode, dto.getShipmentCode())
                    .eq(Sale::getWarehouseCode, dto.getWarehouseCode())
                    .eq(StrUtil.isNotBlank(dto.getCustomerCode()), Sale::getCustomerCode, dto.getCustomerCode())
                    .apply(" actual_basic_quantity - refund_basic_quantity > 0 "));
        return sales;
    }

    /**
     * @param sourceCode :
     * @description wms根据销售单生成oms出库单
     * @author xuyang
     * @date 2020/12/15 14:55
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void wmsSaveOmsOutboundBySale(String sourceCode, Outbound outbound, Map<String, BigDecimal[]> sourceItemOutQuantity) {
        //判断是否合单
        if (sourceCode.startsWith(OrderIdGeneratorUtil.OrderTypeEnum.MG.getCode())) {
            List<Sale> saleList = this.list(new QueryWrapper<Sale>().lambda().eq(Sale::getMergeCode, sourceCode));
            if (cn.hutool.core.collection.CollectionUtil.isNotEmpty(saleList)) {
                for (Sale sale : saleList) {
                    List<SaleItem> saleItems = iSaleItemService.list(new LambdaQueryWrapper<SaleItem>().eq(SaleItem::getSaleCode, sale.getCode()));
                    sourceItemOutQuantity = new HashMap<>();
                    BigDecimal[] quantityList;
                    for (SaleItem saleItem : saleItems) {
                        quantityList = new BigDecimal[2];
                        quantityList[NUM_ZERO] = saleItem.getPlanBasicQuantity();
                        quantityList[NUM_ONE] = saleItem.getPlanQuantity();
                        sourceItemOutQuantity.put(saleItem.getCode(), quantityList);
                    }
                    SaleVO vo = getByCode(sale.getCode());
                    iOmsOutboundService.saveBySaleForWms(vo, outbound, sourceItemOutQuantity);
                }
            }
        } else {
            SaleVO vo = getByCode(sourceCode);
            iOmsOutboundService.saveBySaleForWms(vo, outbound, sourceItemOutQuantity);
        }

    }

    /**
     * @description
     * 更新销售单
     * @author xuyang
     * @date 2020/12/16 9:16
     * @param entity:
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(Sale entity) {
        // 校验单据状态
        Sale dbEntity = this.getOne(new QueryWrapper<Sale>().lambda().eq(Sale::getCode, entity.getCode()));
        if (!(OrderStatusEnum.NEWS.getValue().equals(dbEntity.getStatus()) || OrderStatusEnum.NO_PASS.getValue().equals(dbEntity.getStatus()))) {
            throw new ServiceException("销售单当前状态无法进行更新操作");
        }
        List<String> areaList = entity.getAreaList();
        if (IterUtil.isNotEmpty(areaList) && areaList.size() == 4) {
            entity.setCountryName(areaList.get(0));
            entity.setProvinceName(areaList.get(1));
            entity.setCityName(areaList.get(2));
            entity.setCountyName(areaList.get(3));
        }
        if (StringUtils.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
            entity.setGmtModified(LocalDateTime.now());
        }
        entity.setShopName(shopCache.translate(entity.getShopCode()));
        entity.setCarrierName(carrierCache.translate(entity.getCarrierCode()));
        // 删除所有明细
        iSaleItemService.remove(new QueryWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, entity.getCode()));
        // 保存新的明明系信息
        if (IterUtil.isNotEmpty(entity.getSaleItemList())) {
            iSaleItemService.saveBatch(entity.getSaleItemList());
        }

        int num = this.baseMapper.update(entity, new QueryWrapper<Sale>().lambda().eq(Sale::getCode, entity.getCode()));

        return num;
    }

    /**
     * @param entity :
     * @description 保存主单及明细
     * @author xuyang
     * @date 2020/12/16 14:31
     **/
    @Override
    public Result localSave(Sale entity) {
        // 插入标记，如果是新增，则true，修改则为false
        boolean insertFlag = true;

        if (IterUtil.isEmpty(entity.getSaleItemList())) {
            throw new ServiceException("销售单明细不能为空");
        }
        List<String> areaList = entity.getAreaList();
        if (IterUtil.isNotEmpty(areaList) && areaList.size() == 4) {
            entity.setCountryName(areaList.get(0));
            entity.setProvinceName(areaList.get(1));
            entity.setCityName(areaList.get(2));
            entity.setCountyName(areaList.get(3));
        }
        // 校验明细中是否有重复项
        checkMultipleItem(entity);

        // 判断code，如果有值，则是更新，则判断状态，如果没值，则不用判断
        if (StrUtil.isNotBlank(entity.getCode())) {
            // 更新时，判断单据正确性、状态正确性
            Sale dbEntity = this.getOne(new QueryWrapper<Sale>().lambda().eq(Sale::getCode, entity.getCode()));
            if (null == dbEntity) {
                throw new ServiceException("单据【" + entity.getCode() + "】不存在");
            }
            if (!(ObjectUtil.equal(entity.getStatus(), OrderStatusEnum.NEWS.getValue())
                    || ObjectUtil.equal(entity.getStatus(), OrderStatusEnum.NO_PASS.getValue()))) {
                throw new ServiceException("单据状态不正确，无法保存");
            }
            insertFlag = false;
        }

        // 重新计算一遍数据
        calcQuantity(entity, true);

        entity.setShopName(shopCache.translate(entity.getShopCode()));
        entity.setCarrierName(carrierCache.translate(entity.getCarrierCode()));
        // 判断flag，保存或update 主单
        if (insertFlag) {
            entity.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SO, entity.getShipmentCode(),UserUtil.getBranchCode()));
            entity.setStatus(OrderStatusEnum.NEWS.getValue());
            entity.setGroupCode(UserUtil.getBranchCode());
            save(entity);
        } else {
            this.update(entity, new QueryWrapper<Sale>().lambda().eq(Sale::getCode, entity.getCode()));
        }

        // 删除数据库中的子单
        iSaleItemService.remove(new QueryWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, entity.getCode()));
        // 子单添加信息
        entity.getSaleItemList().forEach(r -> {
            r.setCode(IdUtil.simpleUUID());
            r.setSaleCode(entity.getCode());
            r.setCreatedBy(entity.getCreatedBy());
            r.setCreatedName(entity.getCreatedName());
            r.setModifiedBy(entity.getModifiedBy());
            r.setModifiedName(entity.getModifiedName());
        });

        // 保存子单
        iSaleItemService.saveBatch(entity.getSaleItemList());

        return Result.ok();
    }

    /**
     * @description
     * 校验销售单中是否有重复项，商品+单位相同的则视为相同
     * @author xuyang
     * @date 2021/1/4 11:24
     * @param sale:
     **/
    @Transactional(rollbackFor = {Exception.class})
    public void checkMultipleItem(Sale sale){
        List<SaleItem> saleItems = sale.getSaleItemList();
        Map<String, Object> itemInfo = new HashMap<>();
        String validKey;
        for (SaleItem e: saleItems) {
            validKey = e.getGoodCode() + "-" + e.getBasicUnit();
            if (itemInfo.containsKey(validKey)) {
                throw new ServiceException("存在重复的商品信息，请确认");
            }else{
                itemInfo.put(validKey, e);
            }
        }
    }

    /**
     * @param dto :
     * @description 获取可退货销售单明细
     * @author xuyang
     * @date 2020/12/21 9:29
     **/
    @Override
    public List<SaleItemVO> findCanRefundItemList(SaleItemDto dto) {
        if (StrUtil.isBlank(dto.getSaleCode())) {
            throw new ServiceException("销售单单号为空");
        }
        List<SaleItemVO> saleItems = iSaleItemService.findCanRefundItemList(dto);
        if (IterUtil.isNotEmpty(saleItems)) {
            // 过滤掉实际数量 - 可退数量 = 0 的
            saleItems = saleItems.stream().filter(e->{
                return e.getActualBasicQuantity().subtract(e.getRefundBasicQuantity()).compareTo(BigDecimal.ZERO) > 0;
            }).collect(Collectors.toList());
            // 计算可退数量
            saleItems.forEach(e->{
                e.setCanRefundQuantity(e.getActualQuantity().subtract(e.getRefundQuantity()));
            });
            return saleItems;
        }

        return null;
    }

    /**
     * 审核通过后生成oms出库单，或调用wms生成发货单
     * @param sale
     */
    @Transactional(rollbackFor = {ServiceException.class})
    public void afterAuditPass(Sale sale) {
        // 获取仓库
        Map<String, Object> warehouseMap = warehouseCache.get(sale.getWarehouseCode());
        Integer locationManagement = MapUtil.get(warehouseMap, "locationManagement", Integer.class);
        if(null == locationManagement){
            throw new ServiceException("仓库没有维护“启用库位管理”");
        }
        // 如果没有实体库位，直接生成oms出库单
        if(locationManagement.equals(NUM_ZERO)){
            // 更新主单、子单的实际数量
            this.update(new UpdateWrapper<Sale>().lambda().eq(Sale::getCode, sale.getCode()).setSql(" actual_quantity = plan_quantity ")
                    .setSql(" actual_basic_quantity = plan_basic_quantity "));
            iSaleItemService.update(new UpdateWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, sale.getCode()).setSql(" actual_quantity = plan_quantity ")
                    .setSql(" actual_basic_quantity = plan_basic_quantity "));

            // 直接生成oms出库单
            SaleVO saleVO = getByCode(sale.getCode());
            iOmsOutboundService.saveBySale(saleVO, NUM_ZERO);
        }else{
            // 如果有实体库位，调用wms发货单，生成发货单
            //TODO 注释代码完善
            Delivery delivery = iDeliveryService.createByOmsSale(sale);
            Result result = iSaleOrderService.autoDistribute(delivery.getCode());
        }
    }

    /**
     * 根据明细计算订单总数（销售单位、基本单位）
     * @param entity
     * @param usePlanQuantityFlag: true,使用计划数量计算；false，使用实际数量计算
     */
    @Override
    public void calcQuantity(Sale entity, boolean usePlanQuantityFlag){
        // 计算订单总数（销售单位、基本单位）
        BigDecimal planQuantity = BigDecimal.ZERO, planBasicQuantity = BigDecimal.ZERO;
        // 计算实际订单总数
        BigDecimal actualQuantity = BigDecimal.ZERO, actualBasicQuantity = BigDecimal.ZERO;
        Goods goods;
        Integer unitPrecision;
        // 基本计算数量，根据usePlanQuantityFlag决定使用计划数量还是实际数量计算
        BigDecimal basicCalcQuantity;

        // 明细价税合计的合计
        BigDecimal itemTotalMoney = BigDecimal.ZERO ;
        for(SaleItem e: entity.getSaleItemList()){
            // 根据转换率、单价、税率等，需要计算出基本单位的数量、总价等信息
            goods = goodsCache.getObj(e.getGoodsCode());
            if (null == goods) {
                throw new ServiceException("商品【" + e.getGoodsName() + "】信息不存在");
            }
            unitPrecision = goods.getUnitPrecision().intValue();
            if (OmsGoodTypeEnum.GOOD_TYPE_COMPOSE.getCode().equals(e.getRecordType())) {
                e.setPlanBasicQuantity(e.getPlanQuantity());
                basicCalcQuantity = e.getPlanQuantity();
            } else if (usePlanQuantityFlag) {
                e.setPlanBasicQuantity(e.getPlanQuantity().multiply(e.getUnitConvertQuantity()).setScale(unitPrecision, BigDecimal.ROUND_HALF_UP));
                basicCalcQuantity = e.getPlanQuantity();
            } else {
                e.setActualBasicQuantity(e.getActualQuantity().multiply(e.getUnitConvertQuantity()).setScale(unitPrecision, BigDecimal.ROUND_HALF_UP));
                basicCalcQuantity = e.getActualQuantity();
            }

            // 含税单价 = 单价*税率/100 + 单价
            e.setTaxUnitPrice(e.getUnitPrice().multiply(e.getTaxRate()).divide(DECIMAL100).add(e.getUnitPrice()).setScale(6, BigDecimal.ROUND_HALF_UP));
            // 金额 = 单价*销售数量
            e.setAmountMoney(e.getUnitPrice().multiply(basicCalcQuantity).setScale(6, BigDecimal.ROUND_HALF_UP));

            // 税额 = 单价*销售数量*税率/100  保留6位小数
            e.setTaxMoney(e.getUnitPrice().multiply(basicCalcQuantity).multiply(e.getTaxRate()).divide(DECIMAL100).setScale(6, BigDecimal.ROUND_HALF_UP));
            // 价税合计 = 金额 + 税额 ; 重新再计算一遍，避免前面保留小数位数造成精度丢失
            e.setTotalMoney(e.getUnitPrice().multiply(basicCalcQuantity).add(
                        e.getUnitPrice().multiply(basicCalcQuantity).multiply(e.getTaxRate()).divide(DECIMAL100)
                ).setScale(6, BigDecimal.ROUND_HALF_UP));

            // 计算计划总量
            planQuantity = planQuantity.add(ObjectUtil.defaultIfNull(e.getPlanQuantity(), BigDecimal.ZERO));
            planBasicQuantity = planBasicQuantity.add(ObjectUtil.defaultIfNull(e.getPlanBasicQuantity(), BigDecimal.ZERO));
            // 计算实际总量
            actualQuantity = actualQuantity.add(ObjectUtil.defaultIfNull(e.getActualQuantity(), BigDecimal.ZERO));
            actualBasicQuantity = actualBasicQuantity.add(ObjectUtil.defaultIfNull(e.getActualBasicQuantity(), BigDecimal.ZERO));

            itemTotalMoney = itemTotalMoney.add(e.getTotalMoney());
        }
        entity.setPlanQuantity(planQuantity);
        entity.setPlanBasicQuantity(planBasicQuantity);
        entity.setActualQuantity(actualQuantity);
        entity.setActualBasicQuantity(actualBasicQuantity);
        // 计算欠款金额
        BigDecimal paymentAmount = ObjectUtil.defaultIfNull(entity.getPaymentAmount(), BigDecimal.ZERO);
        entity.setOwedAmount(itemTotalMoney.subtract(paymentAmount));
    }

    /**
     * 更新销售表的退货数量
     * @param saleCode
     */
    @Override
    public void refreshOmsSaleRefundQuantity(String saleCode){
        this.baseMapper.refreshOmsSaleRefundQuantity(saleCode);
    }

    /**
     * 合并店铺，仓库，承运商，买家姓名、电话、地址完全一致单据
     * @param codes
     * @return
     */
    @Override
    public Integer merge(List<String> codes) {
        List<Sale> saleList = list(new QueryWrapper<Sale>().lambda().in(Sale::getCode, codes));
        if (CollectionUtil.isEmpty(saleList)) {
            throw new ServiceException("无可合并订单");
        }
        if (saleList.size() < 2) {
            throw new ServiceException("仅选择一个订单无法合并");
        }
        //校验是否有合并过的订单
        List<Sale> validList = saleList.stream().filter(v -> StringUtils.isNotBlank(v.getMergeCode())).collect(Collectors.toList());
        if (IterUtil.isNotEmpty(validList)) {
            throw new ServiceException("选中记录包含已合并订单，无法继续合并");
        }
        Map<String, List<Sale>> groupMap = new HashMap<>();
        List<Sale> list;
        for (Sale sale : saleList) {
            StringBuilder key = new StringBuilder();
            key.append(sale.getShopCode());
            key.append(sale.getWarehouseCode());
            key.append(sale.getCarrierCode());
            key.append(sale.getBuyerName());
            key.append(sale.getBuyerMobile());
            key.append(sale.getProvinceName());
            key.append(sale.getCityName());
            key.append(sale.getCountyName());
            key.append(sale.getBuyerAddress());
            String keyStr = key.toString();
            if (groupMap.containsKey(keyStr)) {
                list = groupMap.get(keyStr);
            } else {
                list = new ArrayList<>();
            }
            list.add(sale);
            groupMap.put(keyStr, list);
        }
        List<Sale> updateList = new ArrayList<>();
        for (Map.Entry<String, List<Sale>> entry : groupMap.entrySet()) {
            List<Sale> newSaleList = entry.getValue();
            if (newSaleList.size() > 1) {
                //需要合并
                String mergeCode = OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.MG, UserUtil.getLoginWarehouseCodeOrShipmentCode(),UserUtil.getBranchCode());
                newSaleList.forEach(v -> v.setMergeCode(mergeCode));
                updateList.addAll(newSaleList);
            }
        }
        if (IterUtil.isNotEmpty(updateList)) {
            boolean flag = updateBatchById(updateList);
            if (flag) {
                return updateList.size();
            }
        }
        return 0;
    }

    @Override
    public boolean back(List<String> codes) {
        List<Sale> list = list(new QueryWrapper<Sale>().lambda().in(Sale::getCode, codes));
        List<SaleItem> saleItemList = iSaleItemService.list(new LambdaQueryWrapper<SaleItem>().in(SaleItem::getSaleCode, codes));
        Map<String,List<SaleItem>> itemMap = saleItemList.stream().collect(Collectors.groupingBy(SaleItem::getSaleCode));
        if (CollectionUtil.isEmpty(list)) {
            throw new ServiceException("未查询到可退换订单");
        }
        list.forEach(v -> {
            v.setStatus(OrderStatusEnum.NEWS.getValue());
            List<SaleItem> itemList = MapUtil.get(itemMap, v.getCode(), List.class);
            itemList.forEach(i -> {
                //释放占用库存
                iOmsInventoryService.releaseOccupiedInventory(v.getWarehouseCode(),i.getGoodsCode(),v.getShipmentCode(),i.getPlanBasicQuantity());
            });
        });
        return updateBatchById(list);
    }

    /**
     * 自动审核销售单
     * @param sales
     */
    @Override
   public void autoAudit(List<Sale> sales) {
        List<String> codesOfAutoAudit = Lists.newArrayList();
       //1.判断订单是否符合自动审核规则
        sales.stream().forEach(sale -> {
            if (validateIsAutoAudit(sale)) {
                codesOfAutoAudit.add(sale.getCode());
            }
        });
        if (IterUtil.isNotEmpty(codesOfAutoAudit)) {
            //2.如符合，进行审核确认
            OmsAuditSaleDTO entity = new OmsAuditSaleDTO();
            entity.setStatus(OrderStatusEnum.PASS.getValue());
            entity.setSaleCodes(codesOfAutoAudit);
            // 获取数据库数据
            List<Sale> saleList = this.list(new QueryWrapper<Sale>().lambda().in(Sale::getCode, entity.getSaleCodes()));
            //获取没有快递或者没有仓库的销售单
            List<Sale> saleWithoutCarrierNameList = saleList.stream().filter(e -> StrUtil.isEmpty(e.getCarrierName()) || StrUtil.isEmpty(e.getWarehouseCode())).collect(Collectors.toList());
            if (IterUtil.isNotEmpty(saleWithoutCarrierNameList)) {
                List<String> saleCodesWithoutCarrierName = saleWithoutCarrierNameList.stream().map(info -> info.getCode()).collect(Collectors.toList());
                log.error("销售单[ "+ saleCodesWithoutCarrierName + " ]没有快递或者仓库信息，请确认");
                throw new ServiceException("销售单[ "+ saleCodesWithoutCarrierName + " ]没有快递或者仓库信息，请确认");
            }
            if (CollectionUtil.isEmpty(saleList)) {
                throw new ServiceException("单据不存在");
            }
            // 校验数据库数据状态
            List<Sale> checkList = saleList.stream().filter(v -> ObjectUtil.notEqual(v.getStatus(), OrderStatusEnum.PENDING.getValue()) && ObjectUtil.notEqual(v.getStatus(), OrderStatusEnum.WMS_ERROR.getValue())).collect(Collectors.toList());
            if(CollectionUtil.isNotEmpty(checkList)){
                log.error("单据【" + checkList.get(0).getCode() + "】不是待审核状态，无法审核。");
                throw new ServiceException("单据【" + checkList.get(0).getCode() + "】不是待审核状态，无法审核。");
            }
            // 判断审核状态
            if(OrderStatusEnum.NO_PASS.getValue().equals(entity.getStatus())){
                // 审核未通过，判断审核意见是否填写
                if(StrUtil.isBlank(entity.getVerifyOpinion())){
                    throw new ServiceException("审核意见不能为空。");
                }
                // 释放被锁的库存
                this.releaseUseQuantity(entity.getSaleCodes());
            }
            List<RLock> locks = Lists.newArrayList();
            List<Map<String, String>> failCodes = Lists.newArrayList();
            StringBuilder failMsg = new StringBuilder();
            try {
                //可操作的CODE
                List<Sale> execCodes = Lists.newArrayList();
                //合单
                List<Sale> mergeList = iOrderDealService.merge(saleList);
                Map<String, Sale> saleMap = mergeList.stream().collect(Collectors.toMap(Sale::getCode, v -> v));
                for (Sale sale : mergeList) {
                    RLock l = redissonClient.getLock(getAssignLockKey(sale.getCode()));
                    if (l.tryLock()) {
                        log.info("任务【{}】获得锁", sale.getCode());
                        execCodes.add(sale);
                        locks.add(l);
                    } else {
                        Map<String, String> map = new HashMap<>();
                        map.put("key", sale.getCode());
                        map.put("message", "任务操作中");
                        failCodes.add(map);
                        failMsg.append("任务【").append(sale.getCode()).append("】正在操作中</br>");
                        log.error("任务【{}】正在操作中", sale.getCode());
                    }
                }
                // 失败的订单集合
                // 线程结果集
                Map<String, Future<Boolean>> futureMap = Maps.newHashMap();
                for (Sale sale : execCodes) {
                    Future<Boolean> future = this.audit(sale, entity.getStatus(), entity.getVerifyOpinion());
                    if (future == null) {
                        Map<String, String> map = new HashMap<>();
                        map.put("key", sale.getCode());
                        map.put("message", "操作失败");
                        failCodes.add(map);
                    } else {
                        futureMap.put(sale.getCode(), future);
                    }
                }
                futureMap.forEach((key, future) -> {
                    try {
                        while (true) {
                            if (future.isDone()) {
                                Boolean status = future.get();
                                if (!status) {
                                    Map<String, String> map = new HashMap<>();
                                    map.put("key", key);
                                    map.put("message", "操作失败");
                                    failCodes.add(map);
                                }
                                break;
                            }
                        }
                    } catch (ServiceException e) {
                        log.error("oms订单推送到wms【{}】线程获取结果【{}】", key, e.getMessage());
                        Map<String, String> map = new HashMap<>();
                        map.put("key", key);
                        map.put("message", e.getMessage());
                        failCodes.add(map);
                    } catch (Exception e) {
                        log.error("oms订单推送到wms【{}】线程获取结果【{}】", key, e.getMessage());
                        Map<String, String> map = new HashMap<>();
                        map.put("key", key);
                        map.put("message", e.getMessage());
                        failCodes.add(map);
                    }
                });
                List<Sale> resultData = new ArrayList<>();
                for (Map<String, String> map : failCodes) {
                    Sale sale = saleMap.get(map.get("key"));
                    sale.setStatus(OrderStatusEnum.WMS_ERROR.getValue());
                    sale.setWmsMessage(map.get("message"));
                    resultData.add(sale);
                }
                for (Map.Entry<String, Future<Boolean>> map : futureMap.entrySet()) {
                    Sale sale = saleMap.get(map.getKey());
                    sale.setStatus(OrderStatusEnum.PASS.getValue());
                    sale.setWmsMessage("success");
                    resultData.add(sale);
                }
                if (CollectionUtil.isNotEmpty(resultData)) {
                    this.updateBatchById(resultData);
                }
            } finally {
                try {
                    locks.forEach(l -> {
                        // 判断锁不为空 && 当前线程获得到了锁
                        if (null != l && l.isLocked()) {
                            l.unlock();
                        }
                    });
                } catch (Exception e) {
                    log.error("释放锁失败！！", e);
                }
            }
        }
   }

    @Override
    public List<OmsSaleExcelVO> saleExcelView(List<OmsSaleExcelDTO> list) {
        // 导入号
        String importCode = IdUtil.simpleUUID();
        OmsSaleExcelDTO firstRow = list.get(0);
        Shipment shipment = validateShipment(firstRow);
        // 用于校验商品
        List<Goods> goodsList = iGoodsService.list(new QueryWrapper<Goods>().lambda().eq(Goods::getShipmentCode, shipment.getPartyCode()).eq(Goods::getStatus, StatusEnum.ACTIVE.getValue()));
        if (CollectionUtils.isEmpty(goodsList)) {
            throw new ServiceException(SHIPMENT_NOT_GOODS);
        }
        Set<String> baseUnitList = goodsList.stream().map(v -> v.getUnitCode()).collect(Collectors.toSet());
        List<GoodsUnit> goodsUnitList = iGoodsUnitService.list(new QueryWrapper<GoodsUnit>().lambda().in(GoodsUnit::getCode, baseUnitList));
        Map<String, GoodsUnit> goodsUnitMap = goodsUnitList.stream().collect(Collectors.toMap(GoodsUnit::getCode, e -> e, (key1, key2) -> key2));
        Map<String, GoodsUnit> goodsCodeKeyUnitMap = new HashMap<>();
        goodsList.forEach(v->{
            goodsCodeKeyUnitMap.put(v.getCode(), goodsUnitMap.get(v.getUnitCode()));
        });
        List<OmsCustomer> omsCustomers = iCustomerService.list(new LambdaQueryWrapper<OmsCustomer>()
                .select(OmsCustomer::getCode, OmsCustomer::getName)
                .eq(OmsCustomer::getShipmentCode, UserUtil.getLoginWarehouseCodeOrShipmentCode())
                .eq(OmsCustomer::getStatus, NumConst.NUM_ONE)
        );
        Map<String, List<OmsCustomer>> customerMap = omsCustomers.stream().collect(Collectors.groupingBy(OmsCustomer::getName, Collectors.toList()));
        final Map<String, Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(Goods::getGoodCode, e -> e, (key1, key2) -> key2));

        List<String> warehouseCodes = UserUtil.getShipmentAuthorizeWarehouseCodes();
        assert warehouseCodes != null;
        final Map<String, Map<String, Object>> warehouseMap = warehouseCodes.stream().map(s -> warehouseCache.get(s)).collect(Collectors.toMap(e -> e.get("serialNumber").toString(), t -> t, (key1, key2) -> key2));

        List<Shop> shopList = iShopService.list(new QueryWrapper<Shop>().lambda()
                .eq(Shop::getShipmentCode,  UserUtil.getLoginWarehouseCodeOrShipmentCode())
                .eq(Shop::getStatus, NumConst.NUM_ONE)
                .orderByAsc(Shop::getName));
        final Map<String, Shop> shopMap = shopList.stream().collect(Collectors.toMap(Shop::getShopCode, t -> t, (key1, key2) -> key2));

        List<Carrier> carrierList = iCarrierService.list(new QueryWrapper<Carrier>().lambda()
                .select(Carrier::getCode,Carrier::getName)
                .eq(Carrier::getShipmentCode, UserUtil.getLoginWarehouseCodeOrShipmentCode())
                .eq(Carrier::getStatus, NumConst.NUM_ONE)
                .orderByAsc(Carrier::getName));
        Map<String, Carrier> carrierMap = carrierList.stream().collect(Collectors.toMap(Carrier::getName, t -> t, (key1, key2) -> key2));
        // 遍历序号
        AtomicInteger indexes = new AtomicInteger(0);
        Map<String, Sale> saleMap = new HashMap<>();

        Map<String, Set<String>> orderGoodsMap = new HashMap<>();
        Set<String> sourceCodeList = new HashSet<>();
        List<OmsSaleExcelVO> vaildList = new ArrayList<>();
        Map<String, OmsSaleExcelDTO> saleSameMap = new HashMap<>();

        // 省
        List<AreaProvince> provinceList = iAreaProvinceService.list();
        Map<String, AreaProvince> provinceNameMap = provinceList.stream().filter(v -> StrUtil.equalsIgnoreCase(v.getCountryCode(), "cn")).collect(Collectors.toMap(AreaProvince::getProvinceName, t -> t));

        for (OmsSaleExcelDTO temp : list) {

            OmsSaleExcelVO vo = new OmsSaleExcelVO();
            Integer index = indexes.getAndIncrement();
            //必填校验， 由于代码校验要求单个方法if校验不能超过15个，所以拆分为两次校验
            validateNotBlank(temp, index);
            validateNotBlank2(temp, index);
            if (!sourceCodeList.contains(temp.getSourceCode())) {
                List<Sale> saleList = baseMapper.selectList(new QueryWrapper<Sale>().lambda().eq(Sale::getSourceCode, temp.getSourceCode()));
                if (CollectionUtil.isNotEmpty(saleList)) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SALE_ORDER + SOURCE_CODE + COMPOSE_GOODS_HAS);
                }
                sourceCodeList.add(temp.getSourceCode());
            }
            if (saleSameMap.keySet().contains(temp.getSourceCode())) {
                validSameSourceCode(temp, saleSameMap.get(temp.getSourceCode()), index);
            }
            //校验客户
            if (StringUtils.isNotBlank(temp.getCustomerName())) {
                List<OmsCustomer> customers = customerMap.get(temp.getCustomerName());
                if (CollectionUtil.isEmpty(customers)) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SALE_ORDER + CUSTOMER_NAME + NOT_EXIST);
                } else if (customers.size() > 1) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SALE_ORDER + CUSTOMER_NAME + EXSIT_MORE_ONE);
                } else {
                    temp.setCustomerCode(customers.get(0).getCode());
                }
            }
            // 校验相同销售单商品重复
            Set<String> goodSet = orderGoodsMap.get(temp.getSourceCode());
            if (CollectionUtil.isNotEmpty(goodSet) && goodSet.contains(temp.getGoodCode())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SAME + SALE_ORDER + GOOD_CODE_NAME + REPEAT_DATA);
            }
            // 校验商品是否可用以及商品单位是否正确
            Goods goods = goodsMap.get(temp.getGoodCode());
            if (goods == null) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOOD_CODE_NAME + NOT_EXIST);
            }
            if (!goods.getName().equals(temp.getGoodName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOOD_CODE_NAME + YU + GOODS_NAME + NOT_SAMEB);
            }
            temp.setGoods(goods);
            GoodsUnit goodsUnit = goodsCodeKeyUnitMap.get(goods.getCode());
            if (null == goodsUnit) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_NOT_HAS_GOODS_UNIT);
            }
            if (!goodsUnit.getUnitName().equals(temp.getSaleUnitName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOOD_CODE_NAME + YU + BASICUNIT_NAME + NOT_SAMEB);
            }
            // 校验仓库
            Map<String, Object> warehouse = warehouseMap.get(temp.getSerialNumber());
            if (null == warehouse) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_SERIAL_NUMBER + NOT_EXIST);
            }
            if (!warehouse.get("name").toString().equals(temp.getWarehouseName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_SERIAL_NUMBER + YU + WAREHOUSE_NAME + NOT_SAMEB);
            }
            // 校验店铺
            Shop shop = shopMap.get(temp.getShopCode());
            if (null == shop) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHOP_CODE + NOT_EXIST);
            }
            if (!shop.getName().equals(temp.getShopName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHOP_NICK + YU + SHOP_CODE + NOT_SAMEB);
            }
            temp.setShop(shop);
            //校验省、市、区县
            validateArea(temp, provinceNameMap, index);
            // 校验承运商
            Carrier carrier = carrierMap.get(temp.getCarrierName());
            if (null == carrier) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + EXPRESS_NAME + NOT_EXIST);
            }
            //校验销售金额
            if (!NumberUtil.isNumber(temp.getDeliveryPriceStr())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + DELIVERY_PRICE_2 + FORMAT_ERROR);
            }
            BigDecimal deliveryPrice = new BigDecimal(temp.getDeliveryPriceStr());
            if (getNumberOfDecimalPlaces(deliveryPrice) > 2) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + DELIVERY_PRICE_2 + PRICE_TWO_DECIMAL);
            }
            temp.setDeliveryPrice(deliveryPrice);
            temp.setCarrierCode(carrier.getCode());
            temp.setWarehouseCode(warehouse.get("code").toString());

            temp.setSaleUnit(goodsUnit);
            //校验销售数量
            if (!NumberUtil.isNumber(temp.getPlanBasicQuantityStr())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUANTITY + FORMAT_ERROR);
            }
            temp.setPlanBasicQuantity(new BigDecimal(temp.getPlanBasicQuantityStr()));
            temp.setShipmentName(shipment.getUserName());
            BeanUtil.copyProperties(temp, vo);
            vo.setImportCode(importCode);
            vaildList.add(vo);
        }

        // 保存到缓存中
        importTempCache.putTempData(importCode, list);
        return vaildList;
    }

    private void validateArea(OmsSaleExcelDTO temp, Map<String, AreaProvince> provinceNameMap, Integer index) {
        String provinceName = temp.getProvinceName();
        AreaProvince areaProvince = provinceNameMap.get(provinceName);
        if (null == areaProvince) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_PROVINCE + NOT_AVAILABLE);
        }
        String cityName = temp.getCityName();
        List<AreaCity> cityList = areaCache.getCityList(areaProvince.getProvinceCode());
        List<AreaCity> areaCityList = cityList.stream().filter(v -> v.getCityName().equals(cityName)).collect(Collectors.toList());
        if (IterUtil.isEmpty(areaCityList)) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_CITY + NOT_AVAILABLE);
        }
        String countyName = temp.getCountyName();
        for (AreaCity city : areaCityList) {
            List<AreaCounty> countyList = areaCache.getCountyList(city.getCityCode());
            List<AreaCounty> areaCountyList = countyList.stream().filter(v -> v.getCountyName().equals(countyName)).collect(Collectors.toList());
            if (IterUtil.isEmpty(areaCountyList)) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_DISTRICT + NOT_AVAILABLE);
            }
        }

    }

    private void validateNotBlank(OmsSaleExcelDTO temp, Integer index) {
        // 校验来源号是否已存在
        if (StringUtils.isBlank(temp.getSourceCode())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SALE_ORDER + SOURCE_CODE + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getGoodCode())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOOD_CODE_NAME + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getGoodName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_NAME_TEXT + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getShipmentCode())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_SERIAL_NUMBER + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getSerialNumber())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_SERIAL_NUMBER + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getWarehouseName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_NAME + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getShopCode())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHOP_CODE + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getShopName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHOP_NICK + NOT_BLANK);
        }

        if (StringUtils.isBlank(temp.getSaleUnitName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + BASICUNIT_NAME + NOT_BLANK);
        }
        if (StrUtil.isBlank(temp.getDeliveryPriceStr())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + DELIVERY_PRICE_2 + NOT_BLANK);
        }
        if (StrUtil.isBlank(temp.getPlanBasicQuantityStr())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUANTITY + NOT_BLANK);
        }
    }

    private void validateNotBlank2(OmsSaleExcelDTO temp, Integer index) {
        if (StringUtils.isBlank(temp.getBuyerName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + BUYER_NAME + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getBuyerMobile())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + BUYER_MOBILE + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getProvinceName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_PROVINCE + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getCityName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_CITY + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getCountyName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_DISTRICT + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getBuyAddress())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + BUYER_ADDRESS + NOT_BLANK);
        }
        if (StringUtils.isBlank(temp.getCarrierName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + EXPRESS_NAME + NOT_EXIST);
        }
    }

    private int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
        String string = bigDecimal.stripTrailingZeros().toPlainString();
        int index = string.indexOf(".");
        return index < 0 ? 0 : string.length() - index - 1;
    }

    private void validSameSourceCode(OmsSaleExcelDTO temp, OmsSaleExcelDTO dto, Integer index) {
        if (!temp.getWarehouseCode().contains(dto.getWarehouseCode())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_SERIAL_NUMBER + HEAD_NOT_SAME);
        }
        if (!temp.getShipmentCode().contains(dto.getShipmentCode())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_SERIAL_NUMBER + HEAD_NOT_SAME);
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result importSale(String importCode) {
        // 根据导入码，获取导入缓存
        List<OmsSaleExcelDTO> tempData = importTempCache.getTempData(importCode, OmsSaleExcelDTO.class);
        // 判断数据，如果没有了，则直接返回
        if(tempData == null){
            return Result.error("缓存数据不存在，或超过有效时间，缓存已被清除，请重新导入。");
        }
        Map<String, List<OmsSaleExcelDTO>> saleListMap = tempData.stream().collect(Collectors.groupingBy(OmsSaleExcelDTO::getSourceCode, Collectors.toList()));
        List<Goods> goodsList = new ArrayList<>();
        String loginName = partyCache.translate(UserUtil.getCode());
        Goods goods;
        List<Sale> saleList = new ArrayList<>();
        List<SaleItem> saleItemList;
        Sale sale;
        SaleItem saleItem;
        Goods g;
        List<SaleItem> insertList = new ArrayList<>();
        for (Map.Entry<String, List<OmsSaleExcelDTO>> entry : saleListMap.entrySet()) {
            saleItemList = new ArrayList<>();
            List<OmsSaleExcelDTO> list = entry.getValue();
            OmsSaleExcelDTO dto = list.get(0);
            sale = new Sale();
            sale.setWarehouseCode(dto.getWarehouseCode());
            sale.setSourceCode(dto.getSourceCode());
            sale.setShipmentCode(dto.getShipmentCode());
            sale.setShipmentName(dto.getShipmentName());
            sale.setBuyerName(dto.getBuyerName());
            sale.setBuyerMobile(dto.getBuyerMobile());
            sale.setBuyerAddress(dto.getBuyAddress());
            sale.setCountryName("中国");
            sale.setProvinceName(dto.getProvinceName());
            sale.setCityName(dto.getCityName());
            sale.setCountyName(dto.getCountyName());
            sale.setBuyerAddress(dto.getBuyAddress());
            sale.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SO, sale.getShipmentCode(),UserUtil.getBranchCode()));
            sale.setSaleType(SaleTypeEnum.NORMAL.getValue());
            sale.setCreatedBy(UserUtil.getCode());
            sale.setCreatedName(partyCache.translate(UserUtil.getCode()));
            sale.setModifiedBy(UserUtil.getCode());
            sale.setModifiedName(partyCache.translate(UserUtil.getCode()));
            sale.setStatus(OrderStatusEnum.NEWS.getValue());
            sale.setCustomerCode(dto.getCustomerCode());
            sale.setGroupCode(UserUtil.getBranch().getCode());
            sale.setShopCode(dto.getShop().getCode());
            sale.setShopName(dto.getShopName());
            sale.setCarrierCode(dto.getCarrierCode());
            sale.setCarrierName(dto.getCarrierName());
            sale.setSaleType(SaleTypeEnum.NORMAL.getValue());
            BigDecimal totalMoney = BigDecimal.ZERO;
            BigDecimal totalQuantity = BigDecimal.ZERO;
            for (OmsSaleExcelDTO item : list) {
                BigDecimal amountMoney = BigDecimal.ZERO;
                g = item.getGoods();
                saleItem = new SaleItem();
                saleItem.setCode(IdGenerator.uuid2());
                saleItem.setSaleCode(sale.getCode());
                saleItem.setGoodsCode(g.getCode());
                saleItem.setGoodCode(item.getGoodCode());
                saleItem.setGoodsBarCode(g.getBarCode());
                saleItem.setGoodsName(item.getGoodName());
                saleItem.setSpec(g.getSpec());
                saleItem.setBasicUnit(g.getUnitCode());
                saleItem.setBasicUnitName(item.getSaleUnitName());
                saleItem.setUnitConvertQuantity(BigDecimal.ONE);
                saleItem.setExpirationQuantity(g.getQuality());
                saleItem.setSaleUnit(g.getUnitCode());
                saleItem.setSaleUnitName(item.getSaleUnitName());
                saleItem.setPlanQuantity(item.getPlanBasicQuantity());
                saleItem.setPlanBasicQuantity(item.getPlanBasicQuantity());
                saleItem.setTaxRate(BigDecimal.ZERO);
                saleItem.setUnitPrice(item.getDeliveryPrice());
                amountMoney = item.getDeliveryPrice().multiply(item.getPlanBasicQuantity());
                saleItem.setAmountMoney(amountMoney);
                saleItem.setTotalMoney(amountMoney);
                saleItem.setRecordType(NUM_ZERO);
                saleItem.setRemarks(item.getRemarks());
                saleItem.setCreatedBy(UserUtil.getCode());
                saleItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
                saleItem.setModifiedBy(UserUtil.getCode());
                saleItem.setModifiedName(partyCache.translate(UserUtil.getCode()));
                saleItemList.add(saleItem);
                insertList.add(saleItem);
                totalMoney = totalMoney.add(amountMoney);
                totalQuantity = totalQuantity.add(saleItem.getPlanQuantity());
            }
            sale.setPlanQuantity(totalQuantity);
            sale.setPlanBasicQuantity(totalQuantity);
            sale.setOwedAmount(totalMoney);
            sale.setSaleItemList(saleItemList);
            calcQuantity(sale,true);
            saleList.add(sale);
        }
        saveBatch(saleList);
        iSaleItemService.saveBatch(insertList);
        // 完成导入后删除导入的缓存
        importTempCache.removeCache(importCode);
        return Result.ok();
    }


    private Shipment validateShipment(OmsSaleExcelDTO firstRow) {
        if (StringUtils.isBlank(firstRow.getShipmentCode())) {
            throw new ServiceException(TIP_PREFIX + NUM_THREE + LINE + SHIPMENT_SERIAL_NUMBER + NOT_BLANK);
        }
        if (!StringUtils.equals(firstRow.getShipmentCode(), UserUtil.getLoginWarehouseCodeOrShipmentCode())) {
            throw new ServiceException(TIP_PREFIX + NUM_THREE + LINE + SHIPMENT_SERIAL_NUMBER + YU + LOGIN_SHIPMENT + NOT_SAMEB);
        }
//        if (StringUtils.isBlank(firstRow.getShipmentName())) {
//            throw new ServiceException(TIP_PREFIX + NUM_THREE + LINE + SHIPMENT_NAME + NOT_BLANK);
//        }
        Shipment shipment = iShipmentService.findShipmentByPartyCode(firstRow.getShipmentCode());
        if (shipment == null) {
            throw new ServiceException(TIP_PREFIX + NUM_THREE + LINE + SHIPMENT_SERIAL_NUMBER + NOT_EXIST);
        }
//        if (!shipment.getUserName().equals(firstRow.getShipmentName())) {
//            throw new ServiceException(SHIPMENT_NAME + YU + SHIPMENT_SERIAL_NUMBER + NOT_SAMEB);
//        }
        // 校验 货主是否审核通过
        if (!NUM_THREE.equals(shipment.getAuthStatus())) {
            throw new ServiceException(SHIPMENT_NAME + UNAPPROVE_NAME);
        }
        return shipment;
    }

    @Override
    public boolean validateIsAutoAudit(Sale sale) {
        if (sale == null) {
            return false;
        }
        //审核校验
        //获取没有快递或者没有仓库的销售单
        if (StrUtil.isEmpty(sale.getCarrierName()) || StrUtil.isEmpty(sale.getWarehouseCode())) {
            return false;
        }
        List<String> saleCodes = Lists.newArrayList();
        saleCodes.add(sale.getCode());
        //查询商品为空的销售单号
        List<String> saleGoodsLackGoodsInfo = iSaleItemService.getSaleCodelackGoodsInfo(saleCodes);
        if (IterUtil.isNotEmpty(saleGoodsLackGoodsInfo)) {
            return false;
        }
       boolean isAutoAutit = false;
        //1.查询订单规则
       String shipmentCode = sale.getShipmentCode();
       String shopCode = sale.getShopCode();
       LocalDateTime dateTime = LocalDateTime.now();
       OmsAutoCheck autoCheck = omsAutoCheckService.getOne(new LambdaQueryWrapper<OmsAutoCheck>()
               .eq(OmsAutoCheck::getShipmentCode, shipmentCode)
               .eq(OmsAutoCheck::getShopCode, shopCode)
               .le(OmsAutoCheck::getStartTime, dateTime)
               .ge(OmsAutoCheck::getEndTime, dateTime)
               .eq(OmsAutoCheck::getStatus, NUM_ZERO));
       if (autoCheck == null) {
           return isAutoAutit;
       }
        if ("1".equals(autoCheck.getAllGoods())) {
            return true;
        }
       List<String> goodsCodeAll = omsAutoCheckGoodsService.list(new LambdaQueryWrapper<OmsAutoCheckGoods>()
               .eq(OmsAutoCheckGoods::getAuthCheckCode, autoCheck.getCode()))
               .stream().map(OmsAutoCheckGoods::getGoodsCode).collect(Collectors.toList());
       List<String> goodsCodeList = sale.getSaleItemList().stream().map(SaleItem::getGoodsCode).collect(Collectors.toList());

       if (!goodsCodeAll.containsAll(goodsCodeList)) {
           return isAutoAutit;
       } else {
           isAutoAutit = true;
       }
       return isAutoAutit;
   }

    @Override
    public int updateSaleByCode(Sale sale){
        if (StrUtil.isEmpty(sale.getCode())) {
            throw new ServiceException("销售单code不能为空");
        }
        List<String> areaList = sale.getAreaList();
        if (IterUtil.isNotEmpty(areaList) && areaList.size() == 4) {
            sale.setCountryName(areaList.get(0));
            sale.setProvinceName(areaList.get(1));
            sale.setCityName(areaList.get(2));
            sale.setCountyName(areaList.get(3));
        }
        sale.setModifiedBy(UserUtil.getCode());
        sale.setModifiedName(partyCache.translate(UserUtil.getCode()));
        sale.setGmtModified(LocalDateTime.now());
        int num = this.baseMapper.update(sale, new QueryWrapper<Sale>().lambda().eq(Sale::getCode, sale.getCode()));
        return num;
    }

    @Override
    public void updateSaleItem(Sale sale){
        if (sale == null) {
            throw new ServiceException("销售单信息不能为空");
        }
        String code = sale.getCode();
        if (StrUtil.isEmpty(code)) {
            throw new ServiceException("销售单code不能为空");
        }
        List<SaleItem> saleItemList = sale.getSaleItemList();
        if (IterUtil.isEmpty(saleItemList)) {
            throw new ServiceException("销售单明细信息不能为空");
        }
        // 删除所有明细
        iSaleItemService.remove(new QueryWrapper<SaleItem>().lambda().eq(SaleItem::getSaleCode, code));
        // 保存新的明明系信息
        for (SaleItem saleItem : saleItemList) {
            saleItem.setCode(IdUtil.simpleUUID());
        }
        iSaleItemService.saveBatch(saleItemList);

    }

    /**
     * 任务指派分布式锁
     *
     * @param code
     * @return
     */
    private String getAssignLockKey(String code) {
        return String.format("oms-order-code-%s", code);
    }
}
